package com.yykj.hospital.server.service.impl;

import com.yykj.base.client.dto.BaseDepartmentDTO;
import com.yykj.base.client.dto.BaseOfficerDTO;
import com.yykj.base.client.feign.BaseFeignClient;
import com.yykj.common.enums.ResultEnums;
import com.yykj.common.exception.NormalException;
import com.yykj.common.service.RedisService;
import com.yykj.common.util.LocalObjectUtil;
import com.yykj.common.util.LocalStringUtil;
import com.yykj.common.util.PageRequestUtil;
import com.yykj.common.vo.PageData;
import com.yykj.hospital.client.dto.HospitalDTO;
import com.yykj.hospital.client.dto.HospitalOfficerDTO;
import com.yykj.hospital.client.dto.HospitalSettingDTO;
import com.yykj.hospital.client.form.HospitalEditForm;
import com.yykj.hospital.client.form.HospitalListForm;
import com.yykj.hospital.client.form.HospitalSettingForm;
import com.yykj.hospital.server.converter.HospitalDTOConverter;
import com.yykj.hospital.server.converter.HospitalOfficerDTOConverter;
import com.yykj.hospital.server.converter.HospitalSettingDTOConverter;
import com.yykj.hospital.server.form.HospitalBannerForm;
import com.yykj.hospital.server.form.HospitalOfficerEditForm;
import com.yykj.hospital.server.model.entity.*;
import com.yykj.hospital.server.model.repository.*;
import com.yykj.hospital.server.service.HospitalOauthService;
import com.yykj.hospital.server.service.HospitalService;
import com.yykj.hospital.server.service.HospitalSettingService;
import com.yykj.hospital.server.service.banci.BanCiService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Created by chendg on 2021/9/8 0008.
 */
@Service
@Slf4j
public class HospitalServiceImpl implements HospitalService {

    @Override
    public void deleteHospital(Integer id) {
        Hospital hospital = findById(id);
        hospitalRepository.delete(hospital);
    }

    @Override
    @Transactional
    public HospitalDTO editHospital(HospitalEditForm hospitalEditForm) {

        Hospital hospital;
        if (hospitalEditForm.getId() != null && hospitalEditForm.getId() > 0) {// 修改
            // 医院名称：禁止修改
            hospital = findById(hospitalEditForm.getId());
            if (hospitalEditForm.getName() != null
                    && !hospitalEditForm.getName().equals(hospital.getName())) {
                throw new NormalException(ResultEnums.PARAM_ERROR, "医院名称禁止修改");
            }
            LocalObjectUtil.copyNullValue(hospitalEditForm, hospital);
            hospitalRepository.save(hospital);
        } else {   // 新增
            if (StringUtils.isEmpty(hospitalEditForm.getName())) {
                throw new NormalException(ResultEnums.PARAM_ERROR, "请输入医院名称");
            }
            if (StringUtils.isEmpty(hospitalEditForm.getAddress())) {
                throw new NormalException(ResultEnums.PARAM_ERROR, "请输入医院地址");
            }
            if (StringUtils.isEmpty(hospitalEditForm.getInfo())) {
                throw new NormalException(ResultEnums.PARAM_ERROR, "请输入医院介绍");
            }
            hospital = new Hospital();
            BeanUtils.copyProperties(hospitalEditForm, hospital);
            hospital.setId(getNewHospitalId());
            hospitalRepository.save(hospital);
            //添加默认科室
            initDefaultHospitalDepartment(hospital.getId());
            //添加默认职称
            initDefaultHospitalOfficer(hospital.getId());
            //添加默认班次
            initDefaultHospitalBanCi(hospital.getId());
            //添加默认级别 普通号 ， 专家号
            initDefaultHospitalDoctorVisitLevel(hospital.getId());
            //添加在线问诊服务时长配置
            initHospitalOnlineSetting(hospital.getId());
            //其它默认配置
            initHospitalSetting(hospital.getId());
            //添加默认菜单
            initHospitalMenu(hospital.getId());
        }

        HospitalDTO result = new HospitalDTO();
        BeanUtils.copyProperties(hospital, result);
        return result;
    }

    private void initHospitalMenu(Integer hospitalId){
        new Thread(new Runnable() {
            @Override
            public void run() {
                hospitalOauthService.initHospitalMenu(hospitalId);
            }
        }).start();
    }

    private void initHospitalOnlineSetting(Integer hospitalId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                hospitalSettingService.initHospitalOnlineSetting(hospitalId);
            }
        }).start();
    }

    private void initHospitalSetting(Integer hospitalId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                hospitalSettingService.initHospitalSetting(hospitalId);
            }
        }).start();
    }

    private void initDefaultHospitalDoctorVisitLevel(Integer hospitalId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                banCiService.initDefaultHospitalDoctorVisitLevel(hospitalId);
            }
        }).start();
    }

    private void initDefaultHospitalBanCi(Integer hospitalId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                banCiService.initDefaultHospitalBanCi(hospitalId);
            }
        }).start();

    }

    private void initDefaultHospitalOfficer(Integer hospitalId) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                List<BaseOfficerDTO> baseOfficerList = baseFeignClient.getBaseOfficer();
                List<HospitalOfficer> hospitalOfficerList = new ArrayList<>();
                for (BaseOfficerDTO dto : baseOfficerList) {
                    HospitalOfficer officer = new HospitalOfficer();
                    officer.setHospitalId(hospitalId);
                    officer.setName(dto.getName());
                    officer.setSorting(dto.getSorting());

                    hospitalOfficerList.add(officer);
                }
                officerRepository.saveAll(hospitalOfficerList);
            }
        }).start();

    }

    private void initDefaultHospitalDepartment(Integer hospitalId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                List<BaseDepartmentDTO> baseDepartment = baseFeignClient.getBaseDepartment();
                List<HospitalDepartment> list = new ArrayList<>();
                for (BaseDepartmentDTO baseDepartmentDTO : baseDepartment) {
                    if (baseDepartmentDTO.getPid() == 0) {
                        HospitalDepartment parentDep = new HospitalDepartment();
                        BeanUtils.copyProperties(baseDepartmentDTO, parentDep);
                        parentDep.setHospitalId(hospitalId);
                        parentDep.setDepCode(LocalStringUtil.getUUID().substring(24));
                        parentDep.setId(null);
                        parentDep.setHot(false);
                        parentDep = hospitalDepartmentRepository.save(parentDep);
                        list.clear();
                        for (BaseDepartmentDTO childDepartment : baseDepartment) {
                            if (Objects.equals(childDepartment.getPid(), baseDepartmentDTO.getId())) {
                                HospitalDepartment child = new HospitalDepartment();
                                BeanUtils.copyProperties(childDepartment, child);
                                child.setHospitalId(parentDep.getHospitalId());
                                child.setPid(parentDep.getId());
                                child.setDepCode(LocalStringUtil.getUUID().substring(24));
                                child.setId(null);
                                child.setHot(false);
                                list.add(child);
                            }
                        }
                        hospitalDepartmentRepository.saveAll(list);
                    }
                }
            }
        }).start();
    }

    @Override
    public PageData<HospitalDTO> getHospitalList(HospitalListForm hospitalListForm) {

        PageRequest pageRequest = PageRequest.of(
                PageRequestUtil.getPage(hospitalListForm.getCurrentPage()),
                PageRequestUtil.getSize(hospitalListForm.getPageSize()));

        Page<Hospital> findList;
        if (StringUtils.isEmpty(hospitalListForm.getName())) {
            log.info("不用名称查询");
            findList = hospitalRepository.findAll(pageRequest);
        } else {
            log.info("用名称查询{}", hospitalListForm.getName());
            findList = hospitalRepository.findAllByName(hospitalListForm.getName(),
                    pageRequest);
        }
        PageData<HospitalDTO> result = new PageData<>();

        result.setCurrentPage(hospitalListForm.getCurrentPage());
        result.setList(HospitalDTOConverter.convert(findList.getContent()));
        result.setPageSize(hospitalListForm.getPageSize());
        result.setTotalCount(findList.getTotalElements());
        result.setTotalPage(findList.getTotalPages());

        return result;
    }

    @Override
    public HospitalDTO getHospitalInfo(Integer id) {
        Hospital hospital = findById(id);
        return HospitalDTOConverter.convert(hospital);
    }

    @Override
    public HospitalSettingDTO addHospitalSetting(
            HospitalSettingForm hospitalSettingForm) {
        if (hospitalSettingForm.getConsultationBase() > 99999999
                || hospitalSettingForm.getConsultationBase() < 1
                || hospitalSettingForm.getScore() > 5
                || hospitalSettingForm.getScore() < 1
                || hospitalSettingForm.getResponseTime() > 60
                || hospitalSettingForm.getResponseTime() < 0) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        HospitalSetting hospitalSetting = hospitalSettingRepository.findById(hospitalSettingForm.getHospitalId()).orElse(null);
        if(hospitalSetting == null){
            hospitalSetting = HospitalSetting.defaultHospitalSetting(hospitalSettingForm.getHospitalId());
        }
        LocalObjectUtil.copyNullValue(hospitalSettingForm,hospitalSetting);
        hospitalSetting = hospitalSettingRepository.save(hospitalSetting);
        return HospitalSettingDTOConverter.convert(hospitalSetting);
    }

    @Override
    public HospitalSettingDTO getHospitalSetting(Integer hospitalId) {

        HospitalSetting hospitalSetting = hospitalSettingRepository.findById(hospitalId)
                .orElse(null);

        if (hospitalSetting == null) {
            hospitalSetting = HospitalSetting.defaultHospitalSetting(hospitalId);
            hospitalSettingRepository.save(hospitalSetting);
        }
        return HospitalSettingDTOConverter.convert(hospitalSetting);
    }

    @Override
    public HospitalOfficerDTO editHospitalOfficer(HospitalOfficerEditForm form) {
        HospitalOfficer hospitalOfficer;
        if (form.getId() != null && form.getId() > 0) {
            hospitalOfficer = officerRepository.findById(form.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        } else {
            hospitalOfficer = new HospitalOfficer();
        }
        LocalObjectUtil.copyNullValue(form, hospitalOfficer);
        hospitalOfficer = officerRepository.save(hospitalOfficer);
        return HospitalOfficerDTOConverter.convert(hospitalOfficer);
    }

    @Override
    public void deleteHospitalOfficer(Integer id) {
        officerRepository.deleteById(id);
    }

    @Override
    public List<HospitalOfficerDTO> listHospitalOfficer(Integer hospitalId) {
        List<HospitalOfficer> list = officerRepository.findAllByHospitalIdOrderBySorting(hospitalId);
        return HospitalOfficerDTOConverter.convert(list);
    }

    @Override
    public HospitalBanner editBanner(HospitalBannerForm form) {
        HospitalBanner hospitalBanner;
        if (form.getId() != null && form.getId() > 0) {//修改
            hospitalBanner = bannerRepository.findById(form.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        } else {//新增
            hospitalBanner = new HospitalBanner();
        }
        LocalObjectUtil.copyNullValue(form, hospitalBanner);
        hospitalBanner = bannerRepository.save(hospitalBanner);
        return hospitalBanner;
    }

    @Override
    public void deleteBanner(Integer id) {
        bannerRepository.deleteById(id);
    }

    @Override
    public List<HospitalBanner> listBanner(Integer hospitalId) {
        return bannerRepository.findAllByHospitalIdOrderBySorting(hospitalId);
    }


    private Hospital findById(Integer id) {
        return hospitalRepository.findById(id)
                .orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
    }

    private Integer getNewHospitalId() {
        String lockValue;
        boolean lockStatus;
        int cnt = 0;
        do {
            cnt++;
            lockValue = "" + (System.currentTimeMillis() + 2000);
            lockStatus = redisService.lock("hospital_key_lock", lockValue);
            if (!lockStatus) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        } while (cnt < 5 && !lockStatus);
        if (!lockStatus) {
            throw new NormalException(ResultEnums.REDIS_LOCK_ERROR);
        }

        String hospitalKey = redisService.get("hospital_key");
        int key;

        if (hospitalKey == null) {
            Hospital hospital = hospitalRepository.findFirstByOrderByIdDesc();
            if (hospital == null) {
                key = 10000;
            } else {
                key = hospital.getId();
            }
        } else {
            key = Integer.parseInt(hospitalKey);
        }
        key++;
        redisService.set("hospital_key", "" + key);

        redisService.unlock("hospital_key_lock", lockValue);
        return key;

    }


    private BanCiService banCiService;

    private RedisService redisService;

    private HospitalRepository hospitalRepository;

    private HospitalSettingRepository hospitalSettingRepository;

    private HospitalDepartmentRepository hospitalDepartmentRepository;

    private BaseFeignClient baseFeignClient;

    private HospitalOfficerRepository officerRepository;

    private HospitalBannerRepository bannerRepository;

    private HospitalSettingService hospitalSettingService;

    private HospitalOauthService hospitalOauthService;

    @Autowired
    public void setHospitalRepository(HospitalRepository hospitalRepository) {
        this.hospitalRepository = hospitalRepository;
    }

    @Autowired
    public void setHospitalSettingRepository(
            HospitalSettingRepository hospitalSettingRepository) {
        this.hospitalSettingRepository = hospitalSettingRepository;
    }

    @Autowired
    public void setRedisService(RedisService redisService) {
        this.redisService = redisService;
    }

    @Autowired
    public void setHospitalDepartmentRepository(HospitalDepartmentRepository hospitalDepartmentRepository) {
        this.hospitalDepartmentRepository = hospitalDepartmentRepository;
    }

    @Autowired
    public void setBaseFeignClient(BaseFeignClient baseFeignClient) {
        this.baseFeignClient = baseFeignClient;
    }

    @Autowired
    public void setOfficerRepository(HospitalOfficerRepository officerRepository) {
        this.officerRepository = officerRepository;
    }

    @Autowired
    public void setBannerRepository(HospitalBannerRepository bannerRepository) {
        this.bannerRepository = bannerRepository;
    }

    @Autowired
    public void setBanCiService(BanCiService banCiService) {
        this.banCiService = banCiService;
    }

    @Autowired
    public void setHospitalSettingService(HospitalSettingService hospitalSettingService) {
        this.hospitalSettingService = hospitalSettingService;
    }
    @Autowired
    public void setHospitalOauthService(HospitalOauthService hospitalOauthService) {
        this.hospitalOauthService = hospitalOauthService;
    }
}
